// Copyright (c) 2022 Presto Labs Pte. Ltd.
// Author: qiang
#include <boost/asio.hpp>
#include <gflags/gflags.h>

#include "coin2/app/driver.h"
#include "coin2/base/log.h"
#include "coin2/exchange/base/order/processor.h"
#include "coin2/strategy/order_executor.pb.h"
#include "coin2/strategy/strategy.h"

DEFINE_string(driver, "", "driver config");
DEFINE_uint32(interval, 3, "cancel time interval");
DEFINE_uint32(duration, 300, "duration of the program");

namespace {
using boost::asio::steady_timer;
using coin2::app::DriverConfig;
using coin2::app::impl::Driver;
using coin2::controller::Controller;
using coin2::exchange::base::executor::ArchiveSyncExecutor;
using coin2::exchange::base::executor::LiveAsyncExecutor;
using coin2::exchange::base::order::IOrderSubsystem;
using coin2::exchange::base::order::OrderSystemManager;
using coin2::exchange::order::OrderManagerConfig;
using coin2::strategy::IStrategy;
}  // namespace

class PendingOrderCleaner : public IStrategy {
 public:
  PendingOrderCleaner(size_t clean_interval_seconds, size_t life_time_seconds)
      : clean_interval_seconds_(clean_interval_seconds), life_time_seconds_(life_time_seconds) {}

  virtual void Init(Driver* driver) override {
    driver_ = driver;
    LiveAsyncExecutor* live_executor = driver->live_executor();
    auto io = live_executor->MainIoc();
    clean_timer_.reset(
        new steady_timer(*io, boost::asio::chrono::seconds(clean_interval_seconds_)));
    clean_timer_->async_wait(boost::bind(&PendingOrderCleaner::CancelOrderExecutor, this));
    life_timer_.reset(new steady_timer(*io, boost::asio::chrono::seconds(life_time_seconds_)));
    life_timer_->async_wait(boost::bind(&PendingOrderCleaner::ClearDown, this));
  }

 private:
  void CancelOrderExecutor() {
    CHECK(driver_);
    OrderSystemManager* order_manager = driver_->order();
    OrderManagerConfig config = driver_->config().order();

    for (const auto& [system_name, _] : config.exchanges()) {
      auto* os = order_manager->GetUniqueSystemByName(system_name);
      if (!os->is_ready()) {
        continue;
      }
      std::cout << "system_name : " << os->system_name()
                << "  live orders size :" << os->order_manager()->live_orders_size() << std::endl;
      if (os->order_manager()->live_orders_size()) {
        CancelOrderByOs(os);
      }
    }

    clean_timer_->expires_after(boost::asio::chrono::seconds(clean_interval_seconds_));
    clean_timer_->async_wait(boost::bind(&PendingOrderCleaner::CancelOrderExecutor, this));
  }

  void CancelOrderByOs(IOrderSubsystem* os) {
    auto working_order = os->order_manager()->GetAllWorkingOrders();
    auto gateway = os->gateway();
    for (auto& order : working_order) {
      if (!gateway->CancelOrder(order->proc_order_id())) {
        LOG(ERROR) << "PendingOrderCleaner CancelOrder failed, proc_order_id: "
                   << order->proc_order_id();
      } else {
        LOG(INFO) << "request to cancel the order has been sent, proc_order_id: "
                  << order->proc_order_id();
      }
    }
  }

  void ClearDown() {
    std::cout << "Pending order cleaner exit" << std::endl;
    exit(0);
  }

 private:
  size_t clean_interval_seconds_;
  size_t life_time_seconds_;
  Driver* driver_{nullptr};
  std::unique_ptr<steady_timer> clean_timer_;
  std::unique_ptr<steady_timer> life_timer_;
};

int main(int argc, char* argv[]) {
  google::InitGoogleLogging(argv[0]);
  gflags::ParseCommandLineFlags(&argc, &argv, true);

  DriverConfig app_config;
  auto path = std::experimental::filesystem::path(FLAGS_driver);
  app_config.MergeFrom(coin2::base::config::DriverConfigFromJson(path));
  app_config.clear_feed();
  std::cout << "app_config: " << app_config.DebugString() << std::endl;
  std::cout << "cancel interval time : " << FLAGS_interval << " seconds" << std::endl;
  std::cout << "program duration time : " << FLAGS_duration << " seconds" << std::endl;

  PendingOrderCleaner strategy(FLAGS_interval, FLAGS_duration);
  Driver driver(app_config, &strategy);
  driver.Run();
  return 0;
}

// bazel build -c dbg //cc/appcoin2/support:pending_order_cleaner